package com.nuoniu.sibanyun.service.po.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nuoniu.sibanyun.common.exception.NuoNiuException;
import com.nuoniu.sibanyun.entity.bo.*;
import com.nuoniu.sibanyun.entity.erp.ErpInvoiceType;
import com.nuoniu.sibanyun.entity.erp.ErpNumberSet;
import com.nuoniu.sibanyun.entity.po.VPurchaseInvoiceDetail;
import com.nuoniu.sibanyun.entity.po.VPurchaseInvoiceMain;
import com.nuoniu.sibanyun.entity.po.VRoRequisitionOrderDetail;
import com.nuoniu.sibanyun.entity.po.VRoRequisitionOrderMain;
import com.nuoniu.sibanyun.mapper.erp.ErpNumberSetMapper;
import com.nuoniu.sibanyun.mapper.po.VPurchaseInvoiceMainMapper;
import com.nuoniu.sibanyun.service.crm.ICrmGoodsService;
import com.nuoniu.sibanyun.service.erp.IErpAccountService;
import com.nuoniu.sibanyun.service.erp.IErpInvoiceTypeService;
import com.nuoniu.sibanyun.service.erp.IErpNumberSetService;
import com.nuoniu.sibanyun.service.erp.IErpVoucherDesignService;
import com.nuoniu.sibanyun.service.po.IVPurchaseInvoiceDetailService;
import com.nuoniu.sibanyun.service.po.IVPurchaseInvoiceMainService;
import com.nuoniu.sibanyun.service.utils.ErpUtils;
import com.nuoniu.sibanyun.userThread.UserInfo;
import com.nuoniu.sibanyun.userThread.UserThreadLocal;
import org.apache.xmlbeans.impl.xb.xmlconfig.Usertypeconfig;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * 采购发票主表 服务实现类
 * </p>
 *
 * @author 小啊刚
 * @since 2020-11-19
 */
@Service
public class VPurchaseInvoiceMainServiceImpl extends ServiceImpl<VPurchaseInvoiceMainMapper, VPurchaseInvoiceMain> implements IVPurchaseInvoiceMainService {

    @Autowired
    private IErpNumberSetService numberSetService;
    @Autowired
    private ErpNumberSetMapper numberSetMapper;
    @Autowired
    private IVPurchaseInvoiceDetailService invoiceDetailService;
    @Autowired
    private IErpAccountService erpAccountService;
    @Autowired
    private IErpVoucherDesignService erpVoucherDesignService;
    @Autowired
    @Lazy
    private IErpInvoiceTypeService erpInvoiceTypeService;
    @Autowired
    private ICrmGoodsService crmGoodsService;

    //入库单 单据类型
    private static final String[]  InOutVoucherCodes = {"PurchaseReceiveVoucher","ProductReceiveVoucher","DelegateReceiveVoucher","OtherReceiveVoucher"};

    @Override
    public String getCode(Integer companyId) {
        Integer currentAccountId = erpAccountService.getCurrentAccountId();
        ErpNumberSet numberSet = numberSetMapper.getByCode("PurchaseInvoice", companyId, currentAccountId);
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getNewestCode(companyId, dateStr,"PurchaseInvoice");
        return numberSetService.getCode(numberSet, code);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveOrder(VPurchaseInvoiceMain purchaseInvoiceMain) {
        Boolean isNewMain = false;
        List<VPurchaseInvoiceDetail> orderDetailList = purchaseInvoiceMain.getOrderDetailList();
        if(null == orderDetailList)throw new NuoNiuException("单据明细不能为空");
        Long mainId = purchaseInvoiceMain.getId();
        UserInfo userInfo = UserThreadLocal.get();
        VPurchaseInvoiceMain oldVPurchaseInvoiceMain = null;

        String dataSource = purchaseInvoiceMain.getDataSource();
        Integer businessType = purchaseInvoiceMain.getBusinessType();
        Integer invoiceTypeId = purchaseInvoiceMain.getInvoiceTypeId();
        ErpInvoiceType invoiceType = erpInvoiceTypeService.getById(invoiceTypeId);
        Integer type = invoiceType.getType();
//        if(( ( null != businessType && ( 32 == businessType || 99 == businessType) ) || 0 == type) && orderDetailList.stream().anyMatch(f->f.getBaseQuantity().compareTo(BigDecimal.ZERO) > 0)){
//            throw new NuoNiuException("单据明细错误：退货业务 和 负数发票类型的明细数量不能为正数");
//        }
        if(mainId == null) {
            isNewMain = true;
            String code ;
            // 如果 数据来源 是 委外发票
            if ("DelegateInvoice".equals(dataSource)){
                code  = getCodeForDelegate(userInfo.getCompanyId());
                if(code == null) {
                    throw new NuoNiuException("委外发票编码已上限");
                }
            }else{
                purchaseInvoiceMain.setDataSource("PurchaseInvoice");
                code  = getCode(userInfo.getCompanyId());
                if(code == null) {
                    throw new NuoNiuException("采购发票编码已上限");
                }

            }
            boolean isHaveId = orderDetailList.stream().anyMatch(f -> null != f.getId());
            if(isHaveId)throw new NuoNiuException("单据明细错误：不能新增已经存在的明细记录");
            purchaseInvoiceMain.setVoucherState(-1);
            purchaseInvoiceMain.setVoucherCode(code);
            purchaseInvoiceMain.setAccountId(erpAccountService.getCurrentAccountId());
            purchaseInvoiceMain.setCompanyId(userInfo.getCompanyId());
            purchaseInvoiceMain.setCreateUserId(userInfo.getUserId());
            purchaseInvoiceMain.setCumExecuteQuantity(new BigDecimal(0.0));
            purchaseInvoiceMain.setCumRetQuantity(new BigDecimal(0.0));
        }else {
            //校验详情（详情不能有其他单据的明细ID）
            boolean isHaveErrorId = orderDetailList.stream().anyMatch(f -> null != f.getOrderId() && !mainId.toString().equals(f.getOrderId().toString()));
            if(isHaveErrorId)throw new NuoNiuException("单据明细错误：不能修改其他单据的明细记录");
            oldVPurchaseInvoiceMain = this.getById(mainId);
            Integer voucherState = oldVPurchaseInvoiceMain.getVoucherState();
            if(voucherState != null && voucherState > 0) {
                throw new NuoNiuException("已审核,不能修改");
            }

            if(oldVPurchaseInvoiceMain.getIsAccounting()) {
                throw new NuoNiuException("已核算,不能修改");
            }

            /**************  修改权限 *******************/
            if(!crmGoodsService.isHaveOperationRight(oldVPurchaseInvoiceMain.getCreateUserId().longValue())){
                throw new NuoNiuException("你没有权限操作该记录");
            }
            purchaseInvoiceMain.setUpdateUserId(userInfo.getUserId());
            purchaseInvoiceMain.setChangeDate(LocalDateTime.now());
            purchaseInvoiceMain.setAccountId(oldVPurchaseInvoiceMain.getAccountId());
            purchaseInvoiceMain.setSourceVoucherCode(oldVPurchaseInvoiceMain.getSourceVoucherCode());
            purchaseInvoiceMain.setSourceVoucherId(oldVPurchaseInvoiceMain.getSourceVoucherId());
            purchaseInvoiceMain.setSourceVoucherType(oldVPurchaseInvoiceMain.getSourceVoucherType());
            purchaseInvoiceMain.setSourceVoucherDate(oldVPurchaseInvoiceMain.getSourceVoucherDate());
            //不可更新字段
            purchaseInvoiceMain.setVoucherCode(null);
        }

        // 校验 明细数量
//        if( ( null != businessType && (  32 == businessType ||  99 == businessType) ) || 0 == type ){
//            boolean isOk = purchaseInvoiceMain.getOrderDetailList().stream().anyMatch(f -> null == f.getBaseQuantity() || !(f.getBaseQuantity().compareTo(BigDecimal.ZERO) < 0));
//            if(isOk)throw new NuoNiuException("明细数量必须小于0");
//        }else{
//            boolean isOk = purchaseInvoiceMain.getOrderDetailList().stream().anyMatch(f -> null == f.getBaseQuantity() || !(f.getBaseQuantity().compareTo(BigDecimal.ZERO) > 0));
//            if(isOk)throw new NuoNiuException("明细数量必须大于0");
//        }
        boolean isOk = purchaseInvoiceMain.getOrderDetailList().stream().anyMatch(f -> null == f.getBaseQuantity() || !(f.getBaseQuantity().compareTo(BigDecimal.ZERO) > 0));
        if(isOk)throw new NuoNiuException("明细数量必须大于0");
        //计算 明细物料 总数
        BigDecimal totalQuantity =  purchaseInvoiceMain.getOrderDetailList().stream().map(VPurchaseInvoiceDetail::getBaseQuantity).reduce(BigDecimal.ZERO,BigDecimal::add);
        purchaseInvoiceMain.setTotalQuantity(totalQuantity.abs());

        Boolean result = this.saveOrUpdate(purchaseInvoiceMain);
        if(result) {
            List<Long> detailIds = new ArrayList<>();
            orderDetailList.forEach(p -> {
                p.setOrderId(purchaseInvoiceMain.getId().intValue());
                p.setCompanyId(userInfo.getCompanyId());
                p.setAccountId(purchaseInvoiceMain.getAccountId());
                p.setSourceVoucherCode(purchaseInvoiceMain.getSourceVoucherCode());
                p.setSourceVoucherType(purchaseInvoiceMain.getSourceVoucherType());
                p.setSourceVoucherId(purchaseInvoiceMain.getSourceVoucherId());
                p.setSourceVoucherDate(purchaseInvoiceMain.getSourceVoucherDate());
                if(p.getId() != null) {
                    detailIds.add(p.getId());
                }
            });

            //删除明细
            QueryWrapper wrapper = new QueryWrapper();
            wrapper.eq("order_id", mainId);
            if(detailIds != null && detailIds.size() > 0) {
                wrapper.notIn("id", detailIds);
                invoiceDetailService.remove(wrapper);
            }else {
                invoiceDetailService.remove(wrapper);
            }
            //保存明细
            invoiceDetailService.saveOrUpdateBatch(orderDetailList);
            //如果 来源订单 不为空
            if(null != purchaseInvoiceMain.getSourceVoucherId()){
                if ("PurchaseInvoice".equals(purchaseInvoiceMain.getDataSource())){
                    if(!"PurchaseReceiveVoucher".equals(purchaseInvoiceMain.getSourceVoucherType()) )throw new NuoNiuException("采购发票的来源单，只能是采购入库单");
                }else{
                    if(!"DelegateReceiveVoucher".equals(purchaseInvoiceMain.getSourceVoucherType()) )throw new NuoNiuException("委外发票的来源单，只能是委外入库单");
                }
                // 查询 当前单据明细
                VoucherInvoiceMainBo voucherMainBo = new VoucherInvoiceMainBo();
                BeanUtils.copyProperties(purchaseInvoiceMain,voucherMainBo);
                voucherMainBo.setIsNewMain(isNewMain);
                List<VoucherInvoiceDetailBo> voucherDetailBos = new ArrayList<>();
                orderDetailList.forEach(detail->{
                    VoucherInvoiceDetailBo voucherDetailBo = new VoucherInvoiceDetailBo();
                    voucherDetailBos.add(voucherDetailBo);
                    BeanUtils.copyProperties(detail,voucherDetailBo);
                });
                VoucherSearchBo voucherSearchBo = new VoucherSearchBo();
                voucherSearchBo.setSourceVoucherId(purchaseInvoiceMain.getSourceVoucherId());
                voucherSearchBo.setSourceVoucherType(purchaseInvoiceMain.getSourceVoucherType());
                voucherSearchBo.setVoucherId(purchaseInvoiceMain.getId().intValue());
                voucherSearchBo.setVoucherType(dataSource);
                erpVoucherDesignService.calculateInvoiceVoucher(voucherMainBo,voucherDetailBos,voucherSearchBo);
            }
        }
    }

    @Override
    public void submit(Long id, Integer status) {
        if(null == id || null == status || (null != status && (status > 0 || status < -1 )))throw new NuoNiuException("参数错误");
        UserInfo userInfo = UserThreadLocal.get();
        VPurchaseInvoiceMain purchaseInvoiceMain = this.getById(id);
        if( null == purchaseInvoiceMain)throw new NuoNiuException("记录不存在");
        //校验状态
        Integer voucherState = purchaseInvoiceMain.getVoucherState();
        if(status == 0){
            if (voucherState > -1)throw new NuoNiuException("该单据已经提交");
        }else if(status == -1){
            if (voucherState > 0)throw new NuoNiuException("该单据已经审核,请先弃审");
        }
        //提交
        purchaseInvoiceMain.setVoucherState(status);
        purchaseInvoiceMain.setSubmitDate(LocalDateTime.now());
        purchaseInvoiceMain.setSubmitUserId(userInfo.getUserId());
        purchaseInvoiceMain.setSubmitUserName(userInfo.getRealName());
        this.updateById(purchaseInvoiceMain);

    }

    @Override
    public void examine(Long id, Integer status) {
        UserInfo userInfo = UserThreadLocal.get();
        VPurchaseInvoiceMain purchaseInvoiceMain = this.getById(id);
        if(purchaseInvoiceMain == null) {
            throw new NuoNiuException("单据不存在");
        }
        Integer voucherState = purchaseInvoiceMain.getVoucherState();
        BigDecimal cumExecuteQuantity = purchaseInvoiceMain.getCumExecuteQuantity();
        /** 统一校验 审核状态*/
        ErpUtils.checkExamineStatus(voucherState,status,cumExecuteQuantity);
        purchaseInvoiceMain.setVoucherState(status);
        purchaseInvoiceMain.setReviewerUserId(userInfo.getUserId());
        purchaseInvoiceMain.setReviewerUserName(userInfo.getRealName());
        purchaseInvoiceMain.setReviewerDate(LocalDateTime.now());
        this.updateById(purchaseInvoiceMain);

    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void delete(Long id) {
        VPurchaseInvoiceMain purchaseInvoiceMain = getById(id);
        if(purchaseInvoiceMain == null) {
           throw new NuoNiuException("已删除");
        }
        if(!purchaseInvoiceMain.getCreateUserId().equals(UserThreadLocal.get().getUserId())) {
            throw new NuoNiuException("只能删除自已的数据");
        }
        // 数据来源
        String dataSource = purchaseInvoiceMain.getDataSource();
        //如果 来源订单 不为空
        if(null != purchaseInvoiceMain.getSourceVoucherId()){
            // 查询 当前单据明细
            List<VPurchaseInvoiceDetail> orderDetails = invoiceDetailService.getOrderDetails(id);
            VoucherInvoiceMainBo voucherMainBo = new VoucherInvoiceMainBo();
            BeanUtils.copyProperties(purchaseInvoiceMain,voucherMainBo);
            List<VoucherInvoiceDetailBo> voucherDetailBos = new ArrayList<>();
            orderDetails.forEach(detail->{
                VoucherInvoiceDetailBo voucherDetailBo = new VoucherInvoiceDetailBo();
                voucherDetailBos.add(voucherDetailBo);
                BeanUtils.copyProperties(detail,voucherDetailBo);
            });
            VoucherSearchBo voucherSearchBo = new VoucherSearchBo();
            voucherSearchBo.setCalculateType(2);
            voucherSearchBo.setSourceVoucherId(purchaseInvoiceMain.getSourceVoucherId());
            voucherSearchBo.setSourceVoucherType(purchaseInvoiceMain.getSourceVoucherType());
            voucherSearchBo.setVoucherId(purchaseInvoiceMain.getId().intValue());
            voucherSearchBo.setVoucherType(dataSource);
            erpVoucherDesignService.calculateInvoiceVoucher(voucherMainBo,voucherDetailBos,voucherSearchBo);
        }
        removeById(id);
        invoiceDetailService.removeByOrderId(id);
    }

    @Override
    public List<VPurchaseInvoiceMain> getBySourceCode(String voucherCode) {
        return baseMapper.getBySourceCode(voucherCode);
    }

    @Override
    public Integer getByInvoiceType(String ids) {
        return baseMapper.getByInvoiceType(ids);
    }

    @Override
    @Async
    public void updateInvoiceType(ErpInvoiceType invoiceType) {
        baseMapper.updateInvoiceType(invoiceType);
    }

    @Override
    public List<VPurchaseInvoiceMain> getInvoiceList() {
        UserInfo userInfo = UserThreadLocal.get();
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("company_id", userInfo.getCompanyId());
        wrapper.ne("is_accounting", 1);
        wrapper.isNotNull("source_voucher_id");
        return list(wrapper);
    }

    @Override
    public String getCodeForDelegate(Integer companyId) {
        Integer currentAccountId = erpAccountService.getCurrentAccountId();
        ErpNumberSet numberSet = numberSetMapper.getByCode("DelegateInvoice", companyId, currentAccountId);
        String dateStr = numberSet.getMedian() == 1 ? "%Y-%m-%d" : "%Y-%m";
        String code = baseMapper.getNewestCode(companyId, dateStr,"DelegateInvoice");
        return numberSetService.getCode(numberSet, code);
    }

    @Override
    public void saveOrderForDelegate(VPurchaseInvoiceMain purchaseInvoiceMain) {
        Long mainId = purchaseInvoiceMain.getId();
        if(null == mainId){
            purchaseInvoiceMain.setDataSource("DelegateInvoice");
        }
        this.saveOrder(purchaseInvoiceMain);
    }


}
